package com.lhkj.ct.framework.swagger.plugins;

import com.baomidou.mybatisplus.annotation.IEnum;
import com.google.common.base.Joiner;
import com.lhkj.ct.base.enums.IBaseEnum;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.util.ReflectionUtils;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.service.AllowableListValues;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.ExpandedParameterBuilderPlugin;
import springfox.documentation.spi.service.contexts.ParameterExpansionContext;
import springfox.documentation.swagger.common.SwaggerPluginSupport;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * ModelPropertyBuilderPlugin 适用于 application/json 提交的请求参数 及 json返回值 注释
 * ParameterBuilderPlugin、OperationBuilderPlugin 适用于请求方法上的直接枚举参数
 * ExpandedParameterBuilderPlugin 适用于POJO/VO内的枚举参数
 */

@Configuration
@Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER + 1)
public class EnumParameterBuilderPlugin implements ExpandedParameterBuilderPlugin {

    @Override
    public boolean supports(DocumentationType delimiter) {
        return true;
    }

    private static final Joiner joiner = Joiner.on(",");

//    @Override
//    public void apply(ModelPropertyContext context) {
//        Class<?> type = context.getResolver().getClass();
//        if (IBaseEnum.class.isAssignableFrom(type)) {
//            context.getBuilder().type(context.getResolver().resolve(Integer.class));
//        }
//    }

    //    @Override
//    public void apply(ParameterContext context) {
//        Class<?> type = context.resolvedMethodParameter().getParameterType().getErasedType();
//        if (IBaseEnum.class.isAssignableFrom(type)) {
//                Object[] enumConstants = type.getEnumConstants();
//                List<String> displayValues = Arrays.stream(enumConstants).filter(Objects::nonNull).map(item -> {
//                    Class<?> currentClass = item.getClass();
//                    Field indexField = ReflectionUtils.findField(currentClass, "value");
//                    ReflectionUtils.makeAccessible(indexField);
//                    Object value = ReflectionUtils.getField(indexField, item);
//
//                    Field descField = ReflectionUtils.findField(currentClass, "label");
//                    ReflectionUtils.makeAccessible(descField);
//                    Object desc = ReflectionUtils.getField(descField, item);
//                    return value.toString();
//
//                }).collect(Collectors.toList());
//                ParameterBuilder parameterBuilder = context.parameterBuilder();
//                AllowableListValues values = new AllowableListValues(displayValues, "LIST");
//                parameterBuilder.allowableValues(values);
//        }
//    }
//
//

//
//    @Override
//    public void apply(OperationContext context) {
//        Map<String, List<String>> map = new HashMap<>();
//        List<ResolvedMethodParameter> parameters = context.getParameters();
//        parameters.forEach(parameter -> {
//            ResolvedType parameterType = parameter.getParameterType();
//            Class<?> clazz = parameterType.getErasedType();
//            if (IBaseEnum.class.isAssignableFrom(clazz)) {
//
//                IBaseEnum[] enumConstants = (IBaseEnum[]) clazz.getEnumConstants();
//
//                List<String> displayValues = Arrays.stream(enumConstants)
//                        .filter(Objects::nonNull)
//                        .map(
//                                item -> item.getValue().toString()
//                        ).collect(Collectors.toList());
//
//                map.put(parameter.defaultName().orElse(""), displayValues);
//
//                OperationBuilder operationBuilder = context.operationBuilder();
//                Field parametersField = ReflectionUtils.findField(operationBuilder.getClass(), "parameters");
//                ReflectionUtils.makeAccessible(parametersField);
//                List<Parameter> list = (List<Parameter>) ReflectionUtils.getField(parametersField, operationBuilder);
//
//                map.forEach((k, v) -> {
//                    for (Parameter currentParameter : list) {
//                        if (StringUtils.equals(currentParameter.getName(), k)) {
//                            Field description = ReflectionUtils.findField(currentParameter.getClass(), "description");
//                            ReflectionUtils.makeAccessible(description);
//                            Object field = ReflectionUtils.getField(description, currentParameter);
//                            ReflectionUtils.setField(description, currentParameter, field + " , " + joiner.join(v));
//                            break;
//                        }
//                    }
//                });
//            }
//        });
//    }

    /**
     * 修改枚举在文档的注释
     * @param context
     */
    @Override
    public void apply(ParameterExpansionContext context) {
        Class<?> type = context.getFieldType().getErasedType();
        ParameterBuilder parameterBuilder = context.getParameterBuilder();
        if (IBaseEnum.class.isAssignableFrom(type)) {
            setAvailableValue(parameterBuilder, (Class<? extends IBaseEnum>) type);
            IBaseEnum[] enumConstants = (IBaseEnum[]) type.getEnumConstants();
                List<String> displayValues = getDisplayValues(enumConstants);
                // 获取原有的参数描述
                Field descField = ReflectionUtils.findField(parameterBuilder.getClass(), "description");
                ReflectionUtils.makeAccessible(descField);
                // 拼接枚举参数描述
                String joinText = (ReflectionUtils.getField(descField, parameterBuilder) == null ? "" : (ReflectionUtils.getField(descField, parameterBuilder) + ":"))
                        + joiner.join(displayValues);
                // 设置参数描述
                parameterBuilder.description(joinText);
        }else if (IEnum.class.isAssignableFrom(type)) {
            setAvailableValue(parameterBuilder, (Class<? extends IEnum>) type);
            IEnum[] enumConstants = (IEnum[]) type.getEnumConstants();
            List<String> displayValues = getDisplayValues(enumConstants);
            // 获取原有的参数描述
            Field descField = ReflectionUtils.findField(parameterBuilder.getClass(), "description");
            ReflectionUtils.makeAccessible(descField);
            // 拼接枚举参数描述
            String joinText = (ReflectionUtils.getField(descField, parameterBuilder) == null ? "" : (ReflectionUtils.getField(descField, parameterBuilder) + ":"))
                    + joiner.join(displayValues);
            // 设置参数描述
            parameterBuilder.description(joinText);
        }
    }

    /**
     * 设置枚举下拉框
     * @param parameterBuilder
     * @param type
     */
    private void setAvailableValue(ParameterBuilder parameterBuilder, Class<? extends IEnum> type) {
        if (IBaseEnum.class.isAssignableFrom(type)) {
            IBaseEnum[] enumConstants = (IBaseEnum[]) type.getEnumConstants();
            List<String> displayValues = Arrays.stream(enumConstants).filter(Objects::nonNull).map(item -> {
                return item.getValue().toString();
            }).collect(Collectors.toList());
            // 设置可用值
            AllowableListValues values = new AllowableListValues(displayValues, "LIST");
            parameterBuilder.allowableValues(values);
        }
    }

    private List<String> getDisplayValues(IBaseEnum[] enumConstants) {
        return Arrays.stream(enumConstants).filter(Objects::nonNull).map(
                item -> {
                    return item.getValue() + ":" + item.getLabel();
                }
        ).collect(Collectors.toList());
    }

    private List<String> getDisplayValues(IEnum[] enumConstants) {
        return Arrays.stream(enumConstants).filter(Objects::nonNull).map(
                item -> {
                    return item.getValue().toString();
                }
        ).collect(Collectors.toList());
    }
}
